package org.simple_framework.core;


import lombok.extern.slf4j.Slf4j;
import org.simple_framework.inject.DependencyInjector;
import org.simple_framework.stereotype.Component;
import org.simple_framework.stereotype.Controller;
import org.simple_framework.stereotype.Repository;
import org.simple_framework.stereotype.Service;
import org.simple_framework.util.LoadBeanUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author wxc
 * @date 2021年10月06日 22:02
 */
@Slf4j
public class BeanFactory {

    private final Map<Class<?>, Object> beanContainer = new HashMap<>();

    private final Set<Class<? extends Annotation>> ANNOTATION_SET = new HashSet<>();

    {
        ANNOTATION_SET.add(Component.class);
        ANNOTATION_SET.add(Service.class);
        ANNOTATION_SET.add(Controller.class);
        ANNOTATION_SET.add(Repository.class);
    }

    /**
     * @param packageName 包名
     * @date 2021-10-06 22:03
     * 加载包名及其子包下的所有类
     */
    public void loadBeans(String packageName) {
        Set<Class<?>> classes = LoadBeanUtil.extractPackageClass(packageName);
        if (classes == null || classes.size() == 0) {
            log.warn("extract nothing from packageName" + packageName);
            return;
        }
        // new Instance
        for (Class<?> clazz : classes) tryNewInstance(clazz);
        // dependence injection
        for (Map.Entry<Class<?>, Object> entry : beanContainer.entrySet()) {
            DependencyInjector.doIoc(entry.getKey(), entry.getValue(), this);
        }
    }

    private void tryNewInstance(Class<?> clazz) {

        if (filter(clazz)) {
            for (Class<? extends Annotation> annotation : ANNOTATION_SET) {
                if (clazz.isAnnotationPresent(annotation)) {
                    beanContainer.put(clazz, LoadBeanUtil.newInstance(clazz));
                    break;
                }
            }
        }
    }

    private boolean filter(Class<?> clazz) {
        if (clazz == null) return false;
        int c = clazz.getModifiers();
        return clazz.getAnnotations().length > 0 &&
                !clazz.isAnnotation() &&
                !Modifier.isAbstract(c) &&
                !Modifier.isInterface(c);
    }

    public void showBeanContainer() {
        for (Map.Entry<Class<?>, Object> entry : beanContainer.entrySet()) {
            log.debug("key:{},value:{}", entry.getKey(), entry.getValue());
        }
    }

    public Object getBean(Class<?> clazz) {
        return beanContainer.get(clazz);
    }

}
